import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import re
from datetime import datetime
import math

class TextTab:
    """单个标签页类，管理每个标签页的内容和状态"""
    def __init__(self, parent_frame, parent_viewer=None, file_path=None):
        self.parent_frame = parent_frame
        self.parent = parent_viewer  # TextViewer实例
        self.file_path = file_path
        self.file_content = ""
        self.search_results = []
        self.current_result_index = -1
        self.modified = False
        
        # 创建标签页UI
        self.create_ui()
        
        # 如果提供了文件路径，则加载文件
        if file_path:
            self.load_file(file_path)
    
    def create_ui(self):
        """创建标签页UI"""
        # 创建上下分割的布局
        self.vertical_pane = ttk.PanedWindow(self.parent_frame, orient=tk.VERTICAL)
        self.vertical_pane.pack(fill=tk.BOTH, expand=True)
        
        # 创建文本显示区域
        self.text_frame = ttk.Frame(self.vertical_pane)
        self.vertical_pane.add(self.text_frame, weight=3)
        
        # 创建行号显示区域
        self.line_numbers = tk.Canvas(self.text_frame, width=30, bg='#f0f0f0')
        self.line_numbers.pack(side=tk.LEFT, fill=tk.Y)
        
        # 文本显示区域
        self.text_area = tk.Text(self.text_frame, wrap=tk.WORD, undo=True)
        self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 为文本区域添加滚动条
        text_scrollbar = ttk.Scrollbar(self.text_frame)
        text_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.text_area.config(yscrollcommand=text_scrollbar.set)
        text_scrollbar.config(command=lambda *args: self.parent.on_text_scroll(self, *args))
        
        # 创建下方搜索结果区域（初始隐藏）
        self.results_frame = ttk.Frame(self.vertical_pane)
        
        # 搜索结果标题
        results_header_frame = ttk.Frame(self.results_frame)
        results_header_frame.pack(side=tk.TOP, fill=tk.X)
        
        results_label = ttk.Label(results_header_frame, text="搜索结果")
        results_label.pack(side=tk.LEFT, padx=5)
        
        # 添加关闭按钮
        close_button = ttk.Button(results_header_frame, text="×", width=2, 
                                 command=lambda: self.clear_search_results())
        close_button.pack(side=tk.RIGHT, padx=5)
        
        # 搜索结果列表
        self.results_listbox = tk.Listbox(self.results_frame, height=8)
        self.results_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 为搜索结果添加滚动条
        results_scrollbar = ttk.Scrollbar(self.results_frame, command=self.results_listbox.yview)
        results_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.results_listbox.config(yscrollcommand=results_scrollbar.set)
        
        # 创建搜索结果右键菜单
        self.results_context_menu = tk.Menu(self.results_listbox, tearoff=0)
        self.results_context_menu.add_command(label="复制当前行", command=lambda: self.parent.copy_current_line(self))
        self.results_context_menu.add_command(label="全部复制", command=lambda: self.parent.copy_all_results(self))
        self.results_context_menu.add_separator()
        self.results_context_menu.add_command(label="新标签页打开结果", command=lambda: self.parent.open_results_in_new_tab(self))
    
    def load_file(self, file_path):
        """加载文件内容"""
        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
            
            self.file_path = file_path
            self.file_content = content
            
            # 清空文本区域并插入新内容
            self.text_area.delete(1.0, tk.END)
            self.text_area.insert(tk.END, content)
            
            # 重置搜索结果
            self.clear_search_results()
            
            # 重置修改状态
            self.modified = False
            
            # 更新行号显示
            self.parent.update_line_numbers(self)
            
            return True
        except Exception as e:
            messagebox.showerror("错误", f"无法打开文件: {str(e)}")
            return False
    
    def clear_search_results(self):
        """清除搜索结果"""
        # 清除高亮
        self.text_area.tag_remove("search", "1.0", tk.END)
        self.text_area.tag_remove("current_match", "1.0", tk.END)
        
        # 清空结果列表
        self.results_listbox.delete(0, tk.END)
        
        # 隐藏结果面板
        try:
            self.vertical_pane.forget(self.results_frame)
        except:
            pass  # 如果结果面板尚未添加，则忽略错误
        
        # 重置搜索结果
        self.search_results = []
        self.current_result_index = -1
    
    def get_title(self):
        """获取标签页标题"""
        if self.file_path:
            title = os.path.basename(self.file_path)
            if self.modified:
                title += " *"
            return title
        else:
            return "未命名" + (" *" if self.modified else "")

class TextViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("文本查看工具")
        self.root.geometry("1000x600")
        
        # 设置窗口图标（如果有的话）
        try:
            self.root.iconbitmap("icon.ico")
        except:
            pass

        # 配置标签页样式
        self.style = ttk.Style()
        self.style.configure('TNotebook.Tab', padding=[10, 3], font=('微软雅黑', 10))
        self.style.map('TNotebook.Tab',
            background=[('selected', '#4a7a8c'), ('!selected', '#d9d9d9')],
            foreground=[('selected', 'black'), ('!selected', 'black')],
            font=[('selected', ('微软雅黑', 10, 'bold')), ('!selected', ('微软雅黑', 10))]
        )

        # 标签页管理
        self.tabs = []
        self.current_tab_index = -1

        # 关闭按钮相关配置
        self.close_icon_size = 16
        self.close_icon_padding = 5
        
        # 搜索相关变量
        self.search_var = tk.StringVar()
        self.case_sensitive_var = tk.BooleanVar(value=False)
        self.search_mode_var = tk.StringVar(value="highlight")
        
        # 文本换行设置
        self.wrap_mode = tk.StringVar(value="word")  # word: 按单词换行, none: 不换行
        
        self.create_menu()
        self.create_ui()
        
        # 创建一个空白标签页
        self.new_tab()
        
        # 添加键盘快捷键 - 确保在创建标签页后绑定
        self.bind_shortcuts()
        
    def create_menu(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="新建", command=self.new_tab, accelerator="⌘N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="⌘O")
        file_menu.add_separator()
        file_menu.add_command(label="关闭标签页", command=self.close_current_tab, accelerator="⌘W")
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit, accelerator="Alt+F4")
        menubar.add_cascade(label="文件", menu=file_menu)
        
        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        edit_menu.add_command(label="查找", command=self.show_search_frame, accelerator="⌘F")
        menubar.add_cascade(label="编辑", menu=edit_menu)
        
        # 设置菜单
        settings_menu = tk.Menu(menubar, tearoff=0)
        settings_menu.add_checkbutton(label="按单词换行", variable=self.wrap_mode, 
                                     onvalue="word", offvalue="none",
                                     command=self.toggle_wrap_mode)
        menubar.add_cascade(label="设置", menu=settings_menu)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(label="关于", command=self.show_about)
        menubar.add_cascade(label="帮助", menu=help_menu)
        
        self.root.config(menu=menubar)
    
    def create_ui(self):
        """创建用户界面"""
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建标签页控件
        self.notebook = ttk.Notebook(main_frame, style='TNotebook')
        self.notebook.pack(fill=tk.BOTH, expand=True)

        # 绑定标签页切换事件
        self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)

        
        # 添加标签页右键菜单
        self.tab_context_menu = tk.Menu(self.notebook, tearoff=0)
        self.tab_context_menu.add_command(label="新建标签页", command=self.new_tab)
        self.tab_context_menu.add_command(label="关闭标签页", command=self.close_current_tab)
        self.tab_context_menu.add_separator()
        self.tab_context_menu.add_command(label="关闭其他标签页", command=self.close_other_tabs)
        self.tab_context_menu.add_command(label="关闭所有标签页", command=self.close_all_tabs)
        
        # 绑定标签页右键菜单
        self.notebook.bind("<Button-3>", self.show_tab_context_menu)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def new_tab(self):
        """创建新标签页"""
        # 创建标签页内容框架
        tab_frame = ttk.Frame(self.notebook)
        
        # 创建新的标签页对象
        tab = TextTab(tab_frame, self)
        self.tabs.append(tab)
        
        # 添加到标签页控件
        tab_index = len(self.tabs) - 1
        self.notebook.add(tab_frame, text=self.get_tab_text(tab))
        
        # 切换到新标签页
        self.notebook.select(tab_index)
        self.current_tab_index = tab_index
        
        # 绑定事件
        self.bind_tab_events(tab)
        
        # 更新状态栏
        self.status_var.set("就绪")
        
        return tab
    
    def open_file(self):
        """打开文件"""
        file_paths = filedialog.askopenfilenames(
            title="选择文件",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        
        if not file_paths:
            return
        
        # 打开每个选择的文件
        for file_path in file_paths:
            # 检查文件是否已经在标签页中打开
            for i, tab in enumerate(self.tabs):
                if tab.file_path == file_path:
                    # 如果已经打开，切换到该标签页
                    self.notebook.select(i)
                    return
            
            # 如果当前标签页是空白的，使用当前标签页
            if (self.current_tab_index >= 0 and 
                not self.tabs[self.current_tab_index].file_path and 
                not self.tabs[self.current_tab_index].modified):
                tab = self.tabs[self.current_tab_index]
                success = tab.load_file(file_path)
                if success:
                    # 更新标签页标题
                    self.notebook.tab(self.current_tab_index, text=self.get_tab_text(tab))
                    # 更新窗口标题
                    self.update_window_title()
                    # 绑定事件
                    self.bind_tab_events(tab)
            else:
                # 否则创建新标签页
                tab = self.new_tab()
                success = tab.load_file(file_path)
                if success:
                    # 更新标签页标题
                    self.notebook.tab(self.current_tab_index, text=tab.get_title())
                    # 更新窗口标题
                    self.update_window_title()
    
    def close_current_tab(self):
        """关闭当前标签页"""
        if not self.tabs:
            return
        
        # 获取当前标签页索引
        current = self.notebook.index(self.notebook.select())
        
        # 如果标签页已修改，询问是否保存
        if self.tabs[current].modified:
            response = messagebox.askyesnocancel("保存", "文件已修改，是否保存?")
            if response is None:  # 取消
                return
            elif response:  # 是
                # TODO: 实现保存功能
                pass
        
        # 关闭标签页
        self.notebook.forget(current)
        self.tabs.pop(current)
        
        # 如果没有标签页了，创建一个新的
        if not self.tabs:
            self.new_tab()
        
        # 更新当前标签页索引
        self.current_tab_index = self.notebook.index(self.notebook.select())
        
        # 更新窗口标题
        self.update_window_title()
    
    def close_other_tabs(self):
        """关闭其他标签页"""
        if not self.tabs:
            return
        
        # 获取当前标签页索引
        current = self.notebook.index(self.notebook.select())
        
        # 保存当前标签页
        current_tab = self.tabs[current]
        
        # 关闭所有其他标签页
        for i in range(len(self.tabs) - 1, -1, -1):
            if i != current:
                # 如果标签页已修改，询问是否保存
                if self.tabs[i].modified:
                    title = self.tabs[i].get_title()
                    response = messagebox.askyesnocancel("保存", f"{title} 已修改，是否保存?")
                    if response is None:  # 取消
                        continue
                    elif response:  # 是
                        # TODO: 实现保存功能
                        pass
                
                # 关闭标签页
                self.notebook.forget(i)
                self.tabs.pop(i)
                
                # 更新当前标签页索引
                if i < current:
                    current -= 1
        
        # 更新当前标签页索引
        self.current_tab_index = 0
        
        # 更新窗口标题
        self.update_window_title()
    
    def close_all_tabs(self):
        """关闭所有标签页"""
        if not self.tabs:
            return
        
        # 关闭所有标签页
        for i in range(len(self.tabs) - 1, -1, -1):
            # 如果标签页已修改，询问是否保存
            if self.tabs[i].modified:
                title = self.tabs[i].get_title()
                response = messagebox.askyesnocancel("保存", f"{title} 已修改，是否保存?")
                if response is None:  # 取消
                    continue
                elif response:  # 是
                    # TODO: 实现保存功能
                    pass
            
            # 关闭标签页
            self.notebook.forget(i)
            self.tabs.pop(i)
        
        # 创建一个新的标签页
        self.new_tab()
        
        # 更新窗口标题
        self.update_window_title()
    
    def on_tab_changed(self, event):
        """标签页切换事件处理"""
        if not self.tabs:
            return
        
        # 获取当前标签页索引
        self.current_tab_index = self.notebook.index(self.notebook.select())
        
        # 更新窗口标题
        self.update_window_title()
        
        # 更新状态栏
        if self.tabs[self.current_tab_index].file_path:
            file_name = os.path.basename(self.tabs[self.current_tab_index].file_path)
            self.status_var.set(f"已打开: {file_name}")
        else:
            self.status_var.set("就绪")
    

    
    def get_tab_text(self, tab):
        """获取标签页文本"""
        return tab.get_title()
    
    def show_tab_context_menu(self, event):
        """显示标签页右键菜单"""
        self.tab_context_menu.post(event.x_root, event.y_root)
    
    def update_window_title(self):
        """更新窗口标题"""
        if not self.tabs or self.current_tab_index < 0:
            self.root.title("文本查看工具")
            return
        
        current_tab = self.tabs[self.current_tab_index]
        if current_tab.file_path:
            title = os.path.basename(current_tab.file_path)
            if current_tab.modified:
                title += " *"
            self.root.title(f"文本查看工具 - {title}")
        else:
            self.root.title("文本查看工具 - 未命名" + (" *" if current_tab.modified else ""))
    
    def bind_tab_events(self, tab):
        """绑定标签页事件"""
        # 绑定文本区域的滚动和修改事件，以更新行号
        tab.text_area.bind('<KeyRelease>', lambda e: self.update_line_numbers(tab))
        tab.text_area.bind('<ButtonRelease>', lambda e: self.update_line_numbers(tab))
        tab.text_area.bind('<Configure>', lambda e: self.update_line_numbers(tab))
        
        # 绑定文本区域的滚动事件，以同步行号显示
        tab.text_area.bind('<MouseWheel>', lambda e: self.update_line_numbers(tab))
        tab.text_area.bind('<Button-4>', lambda e: self.update_line_numbers(tab))
        tab.text_area.bind('<Button-5>', lambda e: self.update_line_numbers(tab))
        
        # 绑定光标移动事件，以更新状态栏中的光标位置
        tab.text_area.bind('<KeyRelease>', lambda e: self.update_cursor_position(tab))
        tab.text_area.bind('<ButtonRelease>', lambda e: self.update_cursor_position(tab))
        
        # 绑定内容修改事件
        tab.text_area.bind('<<Modified>>', lambda e: self.on_text_modified(tab))
        
        # 绑定搜索结果选择事件
        tab.results_listbox.bind('<<ListboxSelect>>', lambda e: self.on_result_select(tab))
        
        # 绑定搜索结果右键菜单
        tab.results_listbox.bind("<Button-3>", lambda e: self.show_results_context_menu(e, tab))
    
    def on_text_modified(self, tab):
        """处理文本修改事件"""
        if tab.text_area.edit_modified():  # 检查文本是否被修改
            tab.modified = True  # 标记为已修改
            self.update_window_title()  # 更新窗口标题
            tab.text_area.edit_modified(False)  # 重置修改标志，以便下次修改能被检测到
    
    def show_search_frame(self):
        """显示搜索对话框"""
        # 获取当前选中的文本（如果有）
        selected_text = ""
        if self.tabs and self.current_tab_index >= 0:
            tab = self.tabs[self.current_tab_index]
            try:
                # 尝试获取选中的文本
                if tab.text_area.tag_ranges(tk.SEL):
                    selected_text = tab.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            except:
                pass
        
        # 如果搜索对话框不存在或已被销毁，则创建新的对话框
        if not hasattr(self, 'search_dialog') or not self.search_dialog.winfo_exists():
            self.search_dialog = SearchDialog(self.root, self)
            # 如果有选中的文本，设置到搜索框中
            if selected_text:
                self.search_dialog.search_var.set(selected_text)
                self.search_dialog.search_entry.select_range(0, tk.END)
        else:
            # 如果对话框已存在，则将其提到前台
            self.search_dialog.lift()
            self.search_dialog.focus_force()
            # 如果有选中的文本，更新搜索框内容
            if selected_text:
                self.search_dialog.search_var.set(selected_text)
                self.search_dialog.search_entry.select_range(0, tk.END)
    
    def hide_search_frame(self):
        """隐藏搜索对话框"""
        if hasattr(self, 'search_dialog') and self.search_dialog.winfo_exists():
            self.search_dialog.destroy()
        self.clear_search_results()
    
    def clear_search_results(self):
        """清除搜索结果"""
        # 清除高亮
        self.text_area.tag_remove("search", "1.0", tk.END)
        self.text_area.tag_remove("current_match", "1.0", tk.END)
        
        # 清空结果列表
        self.results_listbox.delete(0, tk.END)
        
        # 隐藏结果面板
        try:
            self.vertical_pane.forget(self.results_frame)
        except:
            pass  # 如果结果面板尚未添加，则忽略错误
        
        # 重置搜索状态
        self.search_results = []
        self.current_result_index = -1
        self.status_var.set("就绪")
    
    def search_text(self):
        """搜索文本"""
        if not self.tabs or self.current_tab_index < 0:
            return
            
        current_tab = self.tabs[self.current_tab_index]
        search_term = self.search_var.get()
        if not search_term:
            return
        
        # 如果已经有搜索结果，则移动到下一个匹配项
        if current_tab.search_results:
            self.next_result()
            return
        
        # 清除之前的搜索结果
        current_tab.clear_search_results()
        
        # 获取搜索模式
        search_mode = self.search_mode_var.get()
        
        # 设置搜索标志
        flags = 0 if self.case_sensitive_var.get() else re.IGNORECASE
        
        # 查找所有匹配项
        content = current_tab.text_area.get("1.0", tk.END)
        current_tab.file_content = content  # 更新文件内容
        matches = list(re.finditer(re.escape(search_term), content, flags))
        
        if not matches:
            self.status_var.set(f"未找到 '{search_term}'")
            return
        
        # 保存搜索结果
        current_tab.search_results = matches
        
        # 配置高亮标签
        current_tab.text_area.tag_configure("search", background="yellow")
        current_tab.text_area.tag_configure("current_match", background="orange")
        
        # 根据搜索模式处理结果
        if search_mode == "highlight":
            # 在原文中高亮所有匹配项
            for match in matches:
                start_pos = f"1.0+{match.start()}c"
                end_pos = f"1.0+{match.end()}c"
                current_tab.text_area.tag_add("search", start_pos, end_pos)
            
            # 高亮并滚动到第一个匹配项
            current_tab.current_result_index = 0
            self.highlight_current_match(current_tab)
            
        elif search_mode == "list":
            # 显示结果面板
            current_tab.vertical_pane.add(current_tab.results_frame, weight=1)
            
            # 在结果列表中显示所有匹配项
            for i, match in enumerate(matches):
                # 获取匹配行的内容
                line_start = content.rfind('\n', 0, match.start()) + 1
                line_end = content.find('\n', match.end())
                if line_end == -1:
                    line_end = len(content)
                
                line_content = content[line_start:line_end].strip()
                line_number = content[:match.start()].count('\n') + 1
                
                # 添加到结果列表
                current_tab.results_listbox.insert(tk.END, f"行 {line_number}: {line_content}")
            
            # 在结果列表模式下，清除原文中的高亮显示
            current_tab.text_area.tag_remove("search", "1.0", tk.END)
            current_tab.text_area.tag_remove("current_match", "1.0", tk.END)
        
        # 更新状态栏
        self.status_var.set(f"找到 {len(matches)} 个匹配项")
        
        # 更新搜索状态显示
        if hasattr(self, 'search_dialog') and self.search_dialog.winfo_exists():
            if matches:
                direction = self.search_dialog.search_direction_var.get()
                direction_text = "向前" if direction == "forward" else "向后"
                self.search_dialog.search_status_var.set(f"找到 {len(matches)} 个匹配项 ({direction_text})")
            else:
                self.search_dialog.search_status_var.set("未找到匹配项")
    
    def highlight_current_match(self, tab=None):
        """高亮当前匹配项"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if not tab.search_results or tab.current_result_index < 0:
            return
        
        # 移除之前的当前匹配高亮
        tab.text_area.tag_remove("current_match", "1.0", tk.END)
        
        # 获取当前匹配项
        match = tab.search_results[tab.current_result_index]
        start_pos = f"1.0+{match.start()}c"
        end_pos = f"1.0+{match.end()}c"
        
        # 添加当前匹配高亮
        tab.text_area.tag_add("current_match", start_pos, end_pos)
        
        # 滚动到当前匹配项
        tab.text_area.see(start_pos)
        
        # 更新状态栏
        self.status_var.set(f"匹配项 {tab.current_result_index + 1}/{len(tab.search_results)}")
    
    def next_result(self):
        """跳转到下一个搜索结果"""
        if not self.tabs or self.current_tab_index < 0:
            return
            
        tab = self.tabs[self.current_tab_index]
        if not tab.search_results:
            return
        
        # 根据搜索方向决定移动方向
        if hasattr(self, 'search_dialog') and self.search_dialog.winfo_exists():
            direction = self.search_dialog.search_direction_var.get()
            if direction == "forward":
                tab.current_result_index = (tab.current_result_index + 1) % len(tab.search_results)
            else:
                tab.current_result_index = (tab.current_result_index - 1) % len(tab.search_results)
        else:
            tab.current_result_index = (tab.current_result_index + 1) % len(tab.search_results)
        
        self.highlight_current_match(tab)
    
    def prev_result(self):
        """跳转到上一个搜索结果"""
        if not self.tabs or self.current_tab_index < 0:
            return
            
        tab = self.tabs[self.current_tab_index]
        if not tab.search_results:
            return
        
        # 根据搜索方向决定移动方向
        if hasattr(self, 'search_dialog') and self.search_dialog.winfo_exists():
            direction = self.search_dialog.search_direction_var.get()
            if direction == "forward":
                tab.current_result_index = (tab.current_result_index - 1) % len(tab.search_results)
            else:
                tab.current_result_index = (tab.current_result_index + 1) % len(tab.search_results)
        else:
            tab.current_result_index = (tab.current_result_index - 1) % len(tab.search_results)
        
        self.highlight_current_match(tab)
    
    def on_result_select(self, tab=None, event=None):
        """当在结果列表中选择一项时"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if not tab.search_results:
            return
        
        # 获取选中的索引
        selection = tab.results_listbox.curselection()
        if not selection:
            return
        
        index = selection[0]
        if index < 0 or index >= len(tab.search_results):
            return
        
        # 获取对应的匹配项
        match = tab.search_results[index]
        start_pos = f"1.0+{match.start()}c"
        
        # 在文本区域中高亮并滚动到该位置
        tab.text_area.tag_remove("current_match", "1.0", tk.END)
        tab.text_area.tag_add("current_match", start_pos, f"1.0+{match.end()}c")
        tab.text_area.see(start_pos)
        
        # 更新当前索引
        tab.current_result_index = index
        
        # 更新状态栏
        self.status_var.set(f"匹配项 {index + 1}/{len(tab.search_results)}")
        self.current_result_index = index
    
    def show_results_context_menu(self, event, tab=None):
        """显示搜索结果的右键菜单"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if tab.search_results:
            # 在鼠标位置显示右键菜单
            tab.results_context_menu.tk_popup(event.x_root, event.y_root)
        
    def copy_all_results(self, tab=None):
        """复制所有搜索结果到剪贴板"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if not tab.search_results:
            messagebox.showinfo("提示", "没有搜索结果可复制")
            return
        
        # 构建所有结果的文本
        all_results = "\n".join([tab.results_listbox.get(i) for i in range(tab.results_listbox.size())])
        
        # 复制到剪贴板
        self.root.clipboard_clear()
        self.root.clipboard_append(all_results)
    
    def toggle_wrap_mode(self):
        """切换文本换行模式"""
        wrap_mode = self.wrap_mode.get()
        
        # 更新所有标签页的文本区域换行设置
        for tab in self.tabs:
            if wrap_mode == "word":
                tab.text_area.config(wrap=tk.WORD)
            else:
                tab.text_area.config(wrap=tk.NONE)
        
        # 更新行号显示
        if self.tabs and self.current_tab_index >= 0:
            self.update_line_numbers(self.tabs[self.current_tab_index])
    
    def show_about(self):
        """显示关于对话框"""
        messagebox.showinfo(
            "关于文本查看工具",
            "文本查看工具 v1.0\n\n"
            "一个简单的文本查看和搜索工具\n"
            "支持在原文中高亮显示搜索结果或在单独窗口中列出所有匹配项\n\n"
            "© 2025"
        )
    
    def on_text_scroll(self, tab=None, *args):
        """处理文本区域滚动事件"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        # 实际滚动文本区域
        tab.text_area.yview(*args)
        
        # 更新行号显示
        self.update_line_numbers(tab)
    
    def update_line_numbers(self, tab=None, event=None):
        """更新行号显示"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
        
        # 清空行号画布
        tab.line_numbers.delete("all")
        
        # 获取可见区域的第一行和最后一行
        first_line = tab.text_area.index("@0,0")
        last_line = tab.text_area.index(f"@0,{tab.text_area.winfo_height()}")
        
        first_line_num = int(first_line.split('.')[0])
        last_line_num = int(last_line.split('.')[0])
        
        # 绘制行号
        for line_num in range(first_line_num, last_line_num + 1):
            # 获取对应行的y坐标
            dline = tab.text_area.dlineinfo(f"{line_num}.0")
            if dline:
                y = dline[1]
                tab.line_numbers.create_text(
                    15, y, text=str(line_num), anchor="center", fill="#606060"
                )
    
    def update_cursor_position(self, tab=None, event=None):
        """更新光标位置信息"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
        
        if tab.file_path:
            # 获取当前光标位置
            cursor_pos = tab.text_area.index(tk.INSERT)
            line, col = cursor_pos.split('.')
            
            # 更新状态栏
            file_name = os.path.basename(tab.file_path)
            self.status_var.set(f"已打开: {file_name} | 行: {line}, 列: {int(col)+1}")
        else:
            self.update_line_numbers(tab)  # 确保行号更新

    def copy_current_line(self, tab=None):
        """复制当前选中的搜索结果行"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if not tab.search_results:
            messagebox.showinfo("提示", "没有搜索结果可复制")
            return
            
        # 获取当前选中的结果
        selection = tab.results_listbox.curselection()
        if not selection:
            messagebox.showinfo("提示", "请先选择一个搜索结果")
            return
            
        index = selection[0]
        result_text = tab.results_listbox.get(index)
        
        # 复制到剪贴板
        self.root.clipboard_clear()
        self.root.clipboard_append(result_text)
        
    def open_results_in_new_tab(self, tab=None):
        """在新标签页中打开所有搜索结果"""
        if not tab:
            if not self.tabs or self.current_tab_index < 0:
                return
            tab = self.tabs[self.current_tab_index]
            
        if not tab.search_results:
            return
        
        # 创建新标签页
        new_tab = self.new_tab()
        
        # 构建所有结果的文本（去掉行号和文件信息，只保留纯文本内容）
        all_results = []
        for i in range(tab.results_listbox.size()):
            result_text = tab.results_listbox.get(i)
            # 提取纯文本内容（保留日志格式）
            # 检查是否是日志格式（通常包含时间戳、进程ID等）
            if re.match(r'^\s*行\s+\d+\s*:\s*\d+-\d+\s+\d+:\d+:\d+', result_text):
                # 日志格式，保留完整内容，只去掉"行 xxxx: "前缀
                match = re.match(r'^\s*行\s+\d+\s*:\s*(.+)$', result_text)
                if match:
                    pure_text = match.group(1)
                    all_results.append(pure_text)
                else:
                    all_results.append(result_text)
            # 普通格式，按原逻辑处理
            elif ":" in result_text:
                # 找到最后一个冒号的位置，提取后面的文本内容
                last_colon_index = result_text.rfind(":")
                pure_text = result_text[last_colon_index + 1:].strip()
                all_results.append(pure_text)
            else:
                all_results.append(result_text)
        
        # 在新标签页中显示结果
        new_tab.text_area.delete(1.0, tk.END)
        new_tab.text_area.insert(tk.END, "\n".join(all_results))
        
        # 设置新标签页的标题
        if tab.file_path:
            file_name = os.path.basename(tab.file_path)
            new_tab_title = f"{file_name} - 搜索结果"
        else:
            new_tab_title = "搜索结果"
        
        self.notebook.tab(self.current_tab_index, text=new_tab_title)

    def bind_shortcuts(self):
        """绑定键盘快捷键"""
        # 绑定 Command+F 快捷键到搜索功能
        self.root.bind('<Command-f>', lambda event: self.show_search_frame())
        
        # 为当前标签页的文本区域绑定快捷键
        if self.tabs and self.current_tab_index >= 0:
            tab = self.tabs[self.current_tab_index]
            tab.text_area.bind('<Command-f>', lambda event: self.show_search_frame())

class SearchDialog(tk.Toplevel):
    """搜索对话框类"""
    def __init__(self, parent, viewer):
        super().__init__(parent)
        self.viewer = viewer
        self.title("查找")
        
        # 设置窗口大小
        window_width = 600
        window_height = 150
        self.geometry(f"{window_width}x{window_height}")
        self.resizable(True, False)
        
        # 设置为模态对话框
        self.transient(parent)
        
        # 设置对话框位置在父窗口中央
        # 计算弹窗应该出现的位置，使其居中显示
        parent_width = parent.winfo_width()
        parent_height = parent.winfo_height()
        parent_x = parent.winfo_rootx()
        parent_y = parent.winfo_rooty()
        
        # 计算居中位置
        x = parent_x + (parent_width - window_width) // 2
        y = parent_y + (parent_height - window_height) // 2
        
        # 设置弹窗位置
        self.geometry("+{}+{}".format(x, y))
        
        # 创建搜索界面
        self.create_search_ui()
        
        # 设置对话框关闭时的处理
        self.protocol("WM_DELETE_WINDOW", self.on_close)
        
        # 设置对话框样式
        self.configure(padx=10, pady=10)
        
        # 设置焦点到搜索输入框
        self.search_entry.focus_set()
    
    def create_search_ui(self):
        """创建搜索界面"""
        # 创建主框架
        main_frame = ttk.Frame(self)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 第一行：搜索输入框和按钮
        row1 = ttk.Frame(main_frame)
        row1.pack(fill=tk.X, pady=5)
        
        # 搜索标签
        ttk.Label(row1, text="查找内容:").pack(side=tk.LEFT, padx=(0, 5))
        
        # 搜索输入框
        self.search_var = tk.StringVar()
        self.search_entry = ttk.Entry(row1, textvariable=self.search_var, width=30)
        self.search_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        self.search_entry.bind("<Return>", lambda e: self.search_text())
        
        # 搜索按钮
        ttk.Button(row1, text="查找", command=self.search_text).pack(side=tk.LEFT, padx=5)
        

        
        # 第二行：选项
        row2 = ttk.Frame(main_frame)
        row2.pack(fill=tk.X, pady=5)
        
        # 区分大小写选项
        self.case_sensitive_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(row2, text="区分大小写", variable=self.case_sensitive_var).pack(side=tk.LEFT, padx=5)
        
        # 搜索模式选择
        self.search_mode_var = tk.StringVar(value="highlight")
        ttk.Radiobutton(row2, text="原文高亮", variable=self.search_mode_var, value="highlight").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(row2, text="结果列表", variable=self.search_mode_var, value="list").pack(side=tk.LEFT, padx=5)
        
        # 第三行：搜索方向和状态
        row3 = ttk.Frame(main_frame)
        row3.pack(fill=tk.X, pady=5)
        
        # 搜索方向选择
        ttk.Label(row3, text="搜索方向:").pack(side=tk.LEFT, padx=(0, 5))
        self.search_direction_var = tk.StringVar(value="forward")
        ttk.Radiobutton(row3, text="向前", variable=self.search_direction_var, value="forward").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(row3, text="向后", variable=self.search_direction_var, value="backward").pack(side=tk.LEFT, padx=5)
        
        # 搜索状态显示
        self.search_status_var = tk.StringVar(value="")
        ttk.Label(row3, textvariable=self.search_status_var).pack(side=tk.RIGHT, padx=5)
        
        # 不再需要保存结果按钮，已移至右键菜单
    
    def search_text(self):
        """执行搜索"""
        search_term = self.search_var.get()
        if not search_term:
            return
        
        # 如果已有搜索结果且选择的是'原文高亮'模式，则移动到下一个匹配项
        if self.viewer.tabs and self.viewer.current_tab_index >= 0:
            tab = self.viewer.tabs[self.viewer.current_tab_index]
            if tab.search_results and self.search_mode_var.get() == "highlight":
                self.viewer.next_result()
                return
        
        # 获取搜索选项
        case_sensitive = self.case_sensitive_var.get()
        search_mode = self.search_mode_var.get()
        
        # 设置主窗口的搜索变量
        self.viewer.search_var.set(search_term)
        self.viewer.case_sensitive_var.set(case_sensitive)
        self.viewer.search_mode_var.set(search_mode)
        
        # 执行主窗口的搜索方法
        self.viewer.search_text()
        
        # 更新标签页标题
        if self.viewer.tabs and self.viewer.current_tab_index >= 0:
            tab = self.viewer.tabs[self.viewer.current_tab_index]
            self.viewer.notebook.tab(self.viewer.current_tab_index, text=tab.get_title())
    
    def prev_result(self):
        """查找上一个结果"""
        self.viewer.prev_result()
    
    def next_result(self):
        """查找下一个结果"""
        self.viewer.next_result()

    def on_close(self):
        """关闭对话框"""
        # 不清除搜索结果，只关闭对话框
        self.destroy()

def main():
    root = tk.Tk()
    app = TextViewer(root)
    
    # 设置窗口最小大小
    root.minsize(800, 600)
    
    # 启动主循环
    root.mainloop()

if __name__ == "__main__":
    main()